/*************************************************************************
	> File Name: test9.cc
	> Author: H.M. Lau
	> Mail: hm.lau@outlook.com
	> Created Time: 2023/09/24 10:30
 ************************************************************************/

#include <iostream>
#include <cstring>
#include <ctime>
#include <cstdio>
#include <cassert>
#include <unistd.h>
#include <pthread.h>

#define THREAD_NUM 800

int tickets = 100;

class ThreadData
{
public:
    ThreadData (const std::string &n, pthread_mutex_t *pm):_tname(n), _pmtx(pm)
    {}
    std::string _tname;
    pthread_mutex_t *_pmtx;
};

void *getTickets(void *args)
{
    ThreadData *td = (ThreadData*)args;
    while (1) {
        int n = pthread_mutex_lock(td->_pmtx);
        assert(n == 0);
        if (tickets > 0) {
            usleep(rand() % 1500);
            std::cout << td->_tname << ":" << tickets << std::endl;
            --tickets;
            n = pthread_mutex_unlock(td->_pmtx);
            assert(n == 0);
        } else {
            n = pthread_mutex_unlock(td->_pmtx);
            assert(n == 0);
            break;
        }
        usleep(rand() % 2000);
    }
    delete td;
    return nullptr;
}

int main(int argc, char *argv[])
{
    time_t start = time(nullptr);
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx, nullptr);
    srand((unsigned long)time(nullptr) ^ getpid() ^ 0x415);
    pthread_t t[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; ++i) {
        std::string name = "thread";
        name += std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, &mtx);
        pthread_create(t + i, nullptr, getTickets, (void*)td);
    }
    for (int i = 0; i < THREAD_NUM; ++i) {
        pthread_join(t[i], nullptr);
    }
    pthread_mutex_destroy(&mtx);
    time_t end = time(nullptr);
    std::cout << "cast:" << (int)(end - start) << "s" << std::endl;
    return 0;
}
